#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include <libxml/xmlwriter.h>

#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <dirent.h>


#include "pkgutil.h"
#include "fileutil.h"
#include "config.h"
#include "uzutil.h"
#include "unzip.h"

struct PxlPackage_info * parsePackageInfo_xml(xmlDocPtr manifest, xmlNodePtr node)
{
    xmlNodePtr curNode, dataNode;
    struct PxlPackage_info *result;
    xmlChar *buffer;
    xmlChar *temp;
    
    result = (struct PxlPackage_info *)malloc(sizeof(struct PxlPackage_info));
    memset(result,0,sizeof(struct PxlPackage_info));    
    
    curNode = node;
    
    while (curNode) {
        if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"key")) {
            buffer = xmlNodeListGetString(manifest, curNode->xmlChildrenNode, 1);
            if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageName")) {
                
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    temp = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    result->pkg_name = strdup((char *)temp);
                }
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageVersion")) {

                
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    temp = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    result->pkg_version = strdup((char *)temp);                    
                }
            
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageDesc")) {
                
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    temp = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    result->pkg_description = strdup((char *)temp);                    
                }
            
            }            
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"CFBundleIdentifier")) {
                
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    temp = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    result->pkg_id = strdup((char *)temp);
                }            
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageURL")) {
                
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    temp = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    result->pkg_url = strdup((char *)temp);
                }            
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageUpdateURL")) {                
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    temp = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    result->pkg_url = strdup((char *)temp);
                }            
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageIcon")) {
                
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    temp = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    result->pkg_icon = strdup((char *)temp);
                }            
            }
        }
        curNode = curNode->next;
    }
    
    return result;
}

int parsePackageFile_xml(struct PxlPackage_info *pkg, xmlDocPtr manifest, xmlNodePtr node)
{
    xmlNodePtr curNode, dataNode;
    xmlChar *buffer;
    xmlChar *sourcefile = NULL;
    xmlChar *destfile = NULL;
    int executable = -1;
    int createOnly = 0;
    int permanent = 0;
    
    curNode = node;
    while (curNode) {
        if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"key")) {
            buffer = xmlNodeListGetString(manifest, curNode->xmlChildrenNode, 1);
            if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"source")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    sourcefile = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                }
                        
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"destination")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    destfile = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                }
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"executable")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"true")) {
                    executable = 1;
                }
                else if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"false")) {
                    executable = 0;
                }
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"permanent")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"true")) {
                    permanent = 1;
                }
                else if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"false")) {
                    permanent = 0;
                }
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"overwrite")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"true")) {
                    createOnly = 0;
                }
                else if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"false")) {
                    createOnly = 1;
                }
            }
        }
    
        curNode = curNode->next;
    }
    
    if (sourcefile && destfile) {
        struct PxlPackage_file *fileinfo;
        
        fileinfo = (struct PxlPackage_file *)malloc(sizeof(struct PxlPackage_file));
        memset(fileinfo, 0, sizeof(struct PxlPackage_file));
        
        fileinfo->file_src = strdup((char *)sourcefile);
        fileinfo->file_dest = strdup((char *)destfile);
        fileinfo->file_executable = executable;
        fileinfo->file_createOnly = createOnly;
        fileinfo->file_permanent = permanent;
        
        fileinfo->next_file = pkg->pkg_contents;
        pkg->pkg_contents = fileinfo;        
        return 1;
    }
    
    return 0;
}

int parsePackagePostEntry_xml(struct PxlPackage_info *pkg, xmlDocPtr manifest, xmlNodePtr node, int pre)
{
    xmlNodePtr curNode;
    xmlChar *buffer;
    xmlChar *createFile;
    int count;
    int nowait;


    count = 0;    
    nowait = 0;
    createFile = NULL;
    curNode = node;
    while (curNode) {
        if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"string")) {
            count++;
        }
        curNode = curNode->next;
    }

    if (count) {
        char **commandparms;
        int loop = 0;
        struct PxlPackage_postflight *post;
        
        commandparms = (char **)malloc(sizeof(char *) * (count + 1));
        memset(commandparms,0,sizeof(char *) * (count + 1));
        curNode = node;        
        while (curNode) {
            if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"string")) {
                buffer = xmlNodeListGetString(manifest, curNode->xmlChildrenNode, 1);
                commandparms[loop++] = strdup((char *)buffer);
            }
            else if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"dict")) {
                xmlNodePtr dataNode = curNode->xmlChildrenNode;
                
                while (dataNode && !xmlIsBlankNode(dataNode))
                    dataNode = dataNode->next;
                    
                if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"key")) {
                    buffer = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                    
                    /* Postflight command options */
                    
                    if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"createsFile")) {                        
                        while (dataNode && !xmlIsBlankNode(dataNode))
                            dataNode = dataNode->next;
                            
                        if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                            createFile = xmlNodeListGetString(manifest,dataNode->xmlChildrenNode,1);
                        }
                    }
                    else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"noWait")) {                        
                        while (dataNode && !xmlIsBlankNode(dataNode))
                            dataNode = dataNode->next;
                            
                        if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"true")) {
                            nowait = 1; 
                        }
                    }

                }
            }
            curNode = curNode->next;
        }
        
        post = (struct PxlPackage_postflight *)malloc(sizeof(struct PxlPackage_postflight));
        memset(post, 0, sizeof(struct PxlPackage_postflight));

        post->post_command = commandparms;     
        post->post_count = count;
        post->post_nowait = nowait;

        if (createFile) {
            post->post_createsFile = strdup((const char *)createFile);
        }
   
        if (!pre) {
            post->next_post = pkg->pkg_postflight;
            pkg->pkg_postflight = post;
        }
        else {
            post->next_post = pkg->pkg_preremove;
            pkg->pkg_preremove = post;
        }
        return 1;
    }
    
    return 0;
}

int parsePackageInstalledFile_xml(struct PxlPackage_info *pkg, xmlDocPtr manifest, xmlNodePtr node)
{
    xmlNodePtr curNode, dataNode;
    xmlChar *buffer;
    xmlChar *filename = NULL;
    xmlChar *filehash = NULL;
    int permanent = 0;
    int directory = 0;
    
    curNode = node;
    while (curNode) {
        if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"key")) {
            buffer = xmlNodeListGetString(manifest, curNode->xmlChildrenNode, 1);
            if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"file")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    filename = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                }
                        
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"hash")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"string")) {
                    filehash = xmlNodeListGetString(manifest, dataNode->xmlChildrenNode, 1);
                }
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"permanent")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"true")) {
                    permanent = 1;
                }
                else if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"false")) {
                    permanent = 0;
                }
            }
            else if (buffer && !xmlStrcmp(buffer,(const xmlChar *)"directory")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"true")) {
                    directory = 1;
                }
                else if (dataNode && dataNode->name && !xmlStrcmp(dataNode->name,(const xmlChar *)"false")) {
                    directory = 0;
                }
            }
        }
    
        curNode = curNode->next;
    }
    
    if (filename) {
        struct PxlPackage_touched *fileinfo;
        
        fileinfo = (struct PxlPackage_touched *)malloc(sizeof(struct PxlPackage_touched));
        memset(fileinfo, 0, sizeof(struct PxlPackage_touched));
        
        fileinfo->file_path = strdup((char *)filename);
        if (filehash)
            fileinfo->file_hash = strdup((char *)filehash);
        fileinfo->file_permanent = permanent;
        fileinfo->file_directory = directory;
        
        fileinfo->next_file = pkg->pkg_installed;
        pkg->pkg_installed = fileinfo;        
        return 1;
    }
    
    return 0;
}

int parsePackagePostflight_xml(struct PxlPackage_info *pkg, xmlDocPtr manifest, xmlNodePtr node)
{
    xmlNodePtr curNode, dataNode;
    xmlChar *buffer;
    
    curNode = node;
    
    while (curNode) {
        if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"key")) {
            buffer = xmlNodeListGetString(manifest, curNode->xmlChildrenNode, 1);
            if (!xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackagePostflight")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"array")) {
                    dataNode = dataNode->xmlChildrenNode;
                    while (dataNode && xmlIsBlankNode (dataNode)) {
                        dataNode = dataNode->next;
                    }   

                    while (dataNode) {
                        if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"array")) {
                            parsePackagePostEntry_xml(pkg, manifest,dataNode->xmlChildrenNode,0);
                        }
                        dataNode = dataNode->next;
                    }
                }
            }
            else if (!xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackagePreremove")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"array")) {
                    dataNode = dataNode->xmlChildrenNode;
                    while (dataNode && xmlIsBlankNode (dataNode)) {
                        dataNode = dataNode->next;
                    }   

                    while (dataNode) {
                        if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"array")) {
                            parsePackagePostEntry_xml(pkg, manifest,dataNode->xmlChildrenNode,1);
                        }
                        dataNode = dataNode->next;
                    }
                }
            }
        }        
    
        curNode = curNode->next;
    }
    
    return 1;
}

int parsePackageContents_xml(struct PxlPackage_info *pkg, xmlDocPtr manifest, xmlNodePtr node)
{
    xmlNodePtr curNode, dataNode;
    xmlChar *buffer;
    
    curNode = node;
    
    while (curNode) {
        if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"key")) {
            buffer = xmlNodeListGetString(manifest, curNode->xmlChildrenNode, 1);
            if (!xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageFiles")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"array")) {
                    dataNode = dataNode->xmlChildrenNode;
                    while (dataNode && xmlIsBlankNode (dataNode)) {
                        dataNode = dataNode->next;
                    }   

                    while (dataNode) {
                        if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"dict")) {
                            parsePackageFile_xml(pkg, manifest,dataNode->xmlChildrenNode);
                        }
                        dataNode = dataNode->next;
                    }
                }
            }
        }        
    
        curNode = curNode->next;
    }
    
    return 1;
}

int parsePackageInstalled_xml(struct PxlPackage_info *pkg, xmlDocPtr manifest, xmlNodePtr node)
{
    xmlNodePtr curNode, dataNode;
    xmlChar *buffer;
    
    curNode = node;
    
    while (curNode) {
        if (curNode && !xmlStrcmp(curNode->name,(const xmlChar *)"key")) {
            buffer = xmlNodeListGetString(manifest, curNode->xmlChildrenNode, 1);
            if (!xmlStrcmp(buffer,(const xmlChar *)"RDPxlPackageInstalledFiles")) {
                dataNode = curNode->next;
                while (dataNode && xmlIsBlankNode (dataNode)) {
                    dataNode = dataNode->next;
                }   
                
                if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"array")) {
                    dataNode = dataNode->xmlChildrenNode;
                    while (dataNode && xmlIsBlankNode (dataNode)) {
                        dataNode = dataNode->next;
                    }   

                    while (dataNode) {
                        if (dataNode && !xmlStrcmp(dataNode->name,(const xmlChar *)"dict")) {
                            parsePackageInstalledFile_xml(pkg, manifest,dataNode->xmlChildrenNode);
                        }
                        dataNode = dataNode->next;
                    }
                }
            }
        }        
    
        curNode = curNode->next;
    }
    
    return 1;
}

int copy_file_pkg(struct PxlPackage_info *pkg, struct PxlPackage_file *pkgFile, const char *sourcepath, const char *destpath)
{
    int result;
    struct PxlPackage_touched *filerec;
    
    filerec = NULL;
    result = copy_file(sourcepath, destpath, pkgFile->file_executable, pkgFile->file_createOnly);
    
    if (result) {
        filerec = (struct PxlPackage_touched *)malloc(sizeof(struct PxlPackage_touched));
        memset(filerec,0,sizeof(struct PxlPackage_touched));
        
        filerec->file_path = strdup(destpath);
        filerec->file_permanent = pkgFile->file_permanent;

        // TODO: MD5 hash
        
        filerec->next_file = pkg->pkg_installed;
        pkg->pkg_installed = filerec;
    }
    
    return result;
}

int copy_dir_pkg(struct PxlPackage_info *pkg, struct PxlPackage_file *pkgfile, const char *sourcepath, const char *destpath)
{
    DIR *sourcedir;
    struct dirent *dp;
    struct PxlPackage_touched *fileinfo;

    if (!exists_dir(sourcepath)) {
        return 0;
    }
    
    if (!exists_dir(destpath)) {
        char *tempbuf;
        int templen, tempres;
        
        templen = strlen(destpath) + 4;
        tempbuf = (char *)malloc(templen);
        memset(tempbuf, 0, templen);
        snprintf(tempbuf, templen - 1, "%s/.d", destpath);
        tempres = mkdirs(tempbuf);
        free(tempbuf);
        if (!tempres)
            return 0;
            
        fileinfo = (struct PxlPackage_touched *)malloc(sizeof(struct PxlPackage_touched));
        memset(fileinfo,0,sizeof(struct PxlPackage_touched));
        fileinfo->file_path = strdup(destpath);
        fileinfo->file_directory = 1;
        
        fileinfo->next_file = pkg->pkg_installed;
        pkg->pkg_installed = fileinfo;
    }

    pkgfile->file_executable = -1;        
    
    sourcedir = opendir(sourcepath);
    if (!sourcedir)
        return 0;

    while ((dp = readdir(sourcedir)) != NULL) {
        if (dp->d_name && (strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))) {
            char *sourcetemp, *desttemp;
            int sourcetemplen, desttemplen;
            int result;
            
            result = 0;
            
            sourcetemplen = strlen(sourcepath) + strlen(dp->d_name) + 3;
            sourcetemp = (char *)malloc(sourcetemplen);
            memset(sourcetemp,0,sourcetemplen);
            snprintf(sourcetemp,sourcetemplen - 1,"%s/%s", sourcepath, dp->d_name);

            desttemplen = strlen(destpath) + strlen(dp->d_name) + 3;
            desttemp = (char *)malloc(desttemplen);
            memset(desttemp,0,desttemplen);
            snprintf(desttemp,desttemplen - 1,"%s/%s", destpath, dp->d_name);
            
            if (exists_dir(sourcetemp)) {
                result = copy_dir_pkg(pkg, pkgfile, sourcetemp, desttemp);
            }
            else if (exists_file(sourcetemp)) {
                result = copy_file_pkg(pkg, pkgfile, sourcetemp, desttemp);
            }

            if (!result) {
                log_printf("\t%s overwrite %s", pkgfile->file_createOnly ? "Will not" : "Unable to", desttemp); 
            }

            free(sourcetemp);
            free(desttemp);
            
            if (!result) {
                closedir(sourcedir);
                return 0;
            }
        }
    }
    closedir(sourcedir);
    
    return 1;
}   

int installPackageFile(struct PxlPackage_info *pkg, struct PxlPackage_file *pkgfile)
{
    int result = 0;

    if (pkg->pkg_scratchdir && pkgfile->file_src && pkgfile->file_dest) {
        char *sourcepath;
        char *destpath;
        int sourcelen;
        int destlen;
        
        sourcelen = strlen(pkg->pkg_scratchdir) + strlen(pkgfile->file_src) + 3;
        destlen = strlen(PXL_INSTALL) + strlen(pkgfile->file_dest) + 3;
        
        sourcepath = (char *)malloc(sourcelen);
        destpath = (char *)malloc(destlen);
        
        memset(sourcepath,0,sourcelen);
        memset(destpath,0,destlen);
        snprintf(sourcepath,sourcelen - 1,"%s/%s", pkg->pkg_scratchdir, pkgfile->file_src);
        snprintf(destpath,destlen - 1,"%s%s", PXL_INSTALL, pkgfile->file_dest);

        if (exists_dir(sourcepath)) {
            result = copy_dir_pkg(pkg, pkgfile, sourcepath, destpath);
        }
        else if (exists_file(sourcepath)) {
            result = copy_file_pkg(pkg, pkgfile, sourcepath, destpath);
        }

        free(sourcepath);
        free(destpath);
    }
    
    return result;
}

int removePackage(struct PxlPackage_info *pkg, int remove_all)
{
    struct PxlPackage_touched *filewalk;
    struct PxlPackage_postflight *postwalk;
    char *tempbuffer;
    int templen;

    if (pkg->pkg_id) {
        char *plist;
        int plistlen;
        
        plistlen = strlen(PXL_INSTALL) + strlen("/System/Library/LaunchDaemons/") + strlen(pkg->pkg_id) + 10;
        
        plist = (char *)malloc(plistlen);
        memset(plist,0,plistlen);
        snprintf(plist,plistlen - 1,"%s/System/Library/LaunchDaemons/%s.plist",
            PXL_INSTALL, pkg->pkg_id);
                    
        if (!exists_file(plist)) {
            snprintf(plist,plistlen - 1,"%s/System/Library/LaunchDaemons/%s",
                PXL_INSTALL, pkg->pkg_id);
        }
        
        if (exists_file(plist)) {
            pid_t launchpid;
            
            log_printf("REMOVE: Shutting down service %s", plist);
            
            launchpid = vfork();
            if (!launchpid) {
                execl("/bin/launchctl","/bin/launchctl","unload",plist,NULL);                
                _exit(0);
            }
            else {
                int childstatus;
                wait(&childstatus);            
            }
        }
        
        free(plist);
    }

    postwalk = pkg->pkg_preremove;
    while (postwalk) {
        if (postwalk->post_command) {
            pid_t postpid;
            
            log_printf("REMOVE: Running pre-remove command: %s", postwalk->post_command[0]);
            postpid = vfork();
            if (!postpid) {
                execv(postwalk->post_command[0], postwalk->post_command);
                _exit(0);
            }
            else {
                int childstatus;
                
                wait(&childstatus);

                if (!WIFEXITED(childstatus)) {
                    log_printf("\tAbnormal termination of command.");
                }
            }
        }
        postwalk = postwalk->next_post;
    }


    filewalk = pkg->pkg_installed;
    
    while (filewalk) {
        if (filewalk->file_path && !filewalk->file_directory && (!filewalk->file_permanent || remove_all)) {
            if (exists_dir(filewalk->file_path)) {
                delete_dir(filewalk->file_path);
            }
            else if (exists_file(filewalk->file_path)) {
                unlink(filewalk->file_path);
            }
        }
        filewalk = filewalk->next_file;
    }
    
    filewalk = pkg->pkg_installed;
    
    while (filewalk) {
        if (filewalk->file_path && filewalk->file_directory) {
            if (exists_dir(filewalk->file_path)) {
                if (empty_dir(filewalk->file_path)) {
                    delete_dir(filewalk->file_path);
                }
            }
            free(tempbuffer);
        }
        filewalk = filewalk->next_file;
    }    

    return 1;    
}

int installPackage(struct PxlPackage_info *pkg)
{
    int result;
    struct PxlPackage_file *filewalk;
    struct PxlPackage_postflight *postwalk;

    result = 1;    
    filewalk = pkg->pkg_contents;
    
    while (filewalk && result) {
        result = result && installPackageFile(pkg,filewalk);
        filewalk = filewalk->next_file;
    }

    postwalk = pkg->pkg_postflight;
    while (postwalk) {
        if (postwalk->post_command) {
            pid_t postpid;
            int shouldExecute;

            shouldExecute = 1;
        
            if (postwalk->post_createsFile) {
                shouldExecute = !exists_file(postwalk->post_createsFile);
            }

            if (shouldExecute) {
                log_printf("INSTALL: Running postflight command: %s", postwalk->post_command[0]);
                postpid = vfork();
                if (!postpid) {
                    execvp(postwalk->post_command[0], postwalk->post_command);
                    _exit(0);
                }
                else {
                    int childstatus;
                    
                    wait(&childstatus);

                    if (!WIFEXITED(childstatus)) {
                        log_printf("\tAbnormal termination of command.");
                    }
                }
            }
        }
        postwalk = postwalk->next_post;
    }

    if (pkg->pkg_id) {
        char *plist;
        int plistlen;
        
        plistlen = strlen(PXL_INSTALL) + strlen("/System/Library/LaunchDaemons/") + strlen(pkg->pkg_id) + 10;
        
        plist = (char *)malloc(plistlen);
        memset(plist,0,plistlen);
        snprintf(plist,plistlen - 1,"%s/System/Library/LaunchDaemons/%s.plist",
            PXL_INSTALL, pkg->pkg_id);
                    
        if (!exists_file(plist)) {
            snprintf(plist,plistlen - 1,"%s/System/Library/LaunchDaemons/%s",
                PXL_INSTALL, pkg->pkg_id);
        }
        
        if (exists_file(plist)) {
            pid_t launchpid;

            log_printf("INSTALL: Starting up service %s", plist);
           
            
            launchpid = vfork();
            if (!launchpid) {
                sleep(1);
                execl("/bin/launchctl","/bin/launchctl","load",plist,NULL);                
                _exit(0);
            }
            else {
                int childstatus;
                
                wait(&childstatus);
            }
        }
        
        free(plist);
    }
    


    return result;    
}


struct PxlPackage_info * parsePackageInfo(const char *manifestFile)
{
    xmlDocPtr manifest;
    xmlNodePtr rootNode, curNode;
    struct PxlPackage_info *info;
    
    manifest = xmlParseFile(manifestFile);
    if (!manifest) {
        return NULL;
    }
    
    rootNode = xmlDocGetRootElement(manifest);
    if (!rootNode) {
        xmlFreeDoc(manifest);
        return NULL;
    }

    if (xmlStrcmp(rootNode->name,(const xmlChar *) "plist")) {
        xmlFreeDoc(manifest);
        return NULL;
    }

    curNode = rootNode->xmlChildrenNode;
    while (curNode && xmlIsBlankNode (curNode)) {
        curNode = curNode->next;
    }
    
    /* Our first real plist child!  It better be a dict. */
    if ((curNode == NULL) || xmlStrcmp(curNode->name,(const xmlChar *) "dict")) {
        xmlFreeDoc(manifest);
        return NULL;    
    }

    /* TODO: Get our manifest format version, and check against what we speak. */

    /* Get our package information, for DB stuff */
    info = parsePackageInfo_xml(manifest,curNode->xmlChildrenNode);

    /* And our package content information */
    parsePackageContents_xml(info,manifest,curNode->xmlChildrenNode);

    /* And any installed package information */
    parsePackageInstalled_xml(info,manifest,curNode->xmlChildrenNode);

    /* And any postflight package information */
    parsePackagePostflight_xml(info,manifest,curNode->xmlChildrenNode);
    
    xmlFreeDoc(manifest);    
    return info;    
}

int writePackageInstalledManifest(const char *filename, struct PxlPackage_info *pkg)
{
    xmlTextWriterPtr writer;

    mkdirs(filename);    
    writer = xmlNewTextWriterFilename(filename,0);
    if (!writer)
        return 0;
        
    xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
    xmlTextWriterSetIndent(writer,1);
    xmlTextWriterStartDTD(writer,(xmlChar *)"plist",(xmlChar *)"-//Apple Computer//DTD PLIST 1.0//EN",(xmlChar *)"http://www.apple.com/DTDs/PropertyList-1.0.dtd");
    xmlTextWriterEndDTD(writer);
    
    xmlTextWriterStartElement(writer,(xmlChar *)"plist");
    xmlTextWriterStartElement(writer,(xmlChar *)"dict");

    xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackageFormat");    
    xmlTextWriterWriteFormatElement(writer,(xmlChar *)"integer","%d",PXL_MANIFEST_FORMAT);

    if (pkg->pkg_id) {
        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"CFBundleIdentifier");
        xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",pkg->pkg_id);
    }

    if (pkg->pkg_name) {
        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackageName");
        xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",pkg->pkg_name);
    }

    if (pkg->pkg_version) {
        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackageVersion");
        xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",pkg->pkg_version);
    }

    if (pkg->pkg_description) {
        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackageDesc");
        xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",pkg->pkg_description);
    }

    if (pkg->pkg_url) {
        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackageURL");
        xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",pkg->pkg_url);
    }

    if (pkg->pkg_updateurl) {
        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackageUpdateURL");
        xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",pkg->pkg_updateurl);
    }


    if (pkg->pkg_installed) {
        struct PxlPackage_touched *filewalk;
        
        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackageInstalledFiles");
        xmlTextWriterStartElement(writer,(xmlChar *)"array");
        
        filewalk = pkg->pkg_installed;
        
        while (filewalk) {
            if (filewalk->file_path) {
                xmlTextWriterStartElement(writer,(xmlChar *)"dict");
                
                xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"file");
                xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",filewalk->file_path);
                
                if (filewalk->file_hash) {
                    xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"hash");
                    xmlTextWriterWriteFormatElement(writer,(xmlChar *)"string","%s",filewalk->file_hash);                
                }

                if (filewalk->file_permanent) {
                    xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"permanent");
                    xmlTextWriterWriteElement(writer,(xmlChar *)"true",NULL);
                    xmlTextWriterEndElement(writer);
                }

                if (filewalk->file_directory) {
                    xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"directory");
                    xmlTextWriterWriteElement(writer,(xmlChar *)"true",NULL);
                    xmlTextWriterEndElement(writer);
                }
                
                xmlTextWriterEndElement(writer);
            }
            filewalk = filewalk->next_file;
        }
        
        xmlTextWriterEndElement(writer);
    }
    
    if (pkg->pkg_postflight) {
        struct PxlPackage_postflight *postwalk;

        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackagePostflight");
        xmlTextWriterStartElement(writer,(xmlChar *)"array");
        
        postwalk = pkg->pkg_postflight;
        while (postwalk) {
            int loop;
            xmlTextWriterStartElement(writer,(xmlChar *)"array");
        
            for (loop = 0; loop < postwalk->post_count; loop++) {
                xmlTextWriterWriteElement(writer,(xmlChar *)"string",(xmlChar *)postwalk->post_command[loop]);
            }
        
            xmlTextWriterEndElement(writer);
            postwalk = postwalk->next_post;
        }
        
        xmlTextWriterEndElement(writer);
    }

    if (pkg->pkg_preremove) {
        struct PxlPackage_postflight *postwalk;

        xmlTextWriterWriteElement(writer,(xmlChar *)"key",(xmlChar *)"RDPxlPackagePreremove");
        xmlTextWriterStartElement(writer,(xmlChar *)"array");
        
        postwalk = pkg->pkg_preremove;
        while (postwalk) {
            int loop;
            xmlTextWriterStartElement(writer,(xmlChar *)"array");
        
            for (loop = 0; loop < postwalk->post_count; loop++) {
                xmlTextWriterWriteElement(writer,(xmlChar *)"string",(xmlChar *)postwalk->post_command[loop]);
            }
        
            xmlTextWriterEndElement(writer);
            postwalk = postwalk->next_post;
        }
        
        xmlTextWriterEndElement(writer);
    }
    
    xmlTextWriterEndElement(writer);    
    xmlTextWriterEndElement(writer);
    xmlTextWriterEndDocument(writer);
    xmlFreeTextWriter(writer);
    
    return 1;
}

void freePackageInfo(struct PxlPackage_info *info)
{
    if (!info)
        return;
        
    if (info->pkg_contents) {
        struct PxlPackage_file *filewalk, *filenext;
        
        filewalk = info->pkg_contents;
        info->pkg_contents = NULL;
        while (filewalk) {
            filenext = filewalk->next_file;
            if (filewalk->file_src) {
                free(filewalk->file_src);
                filewalk->file_src = NULL;
            }
            if (filewalk->file_dest) {
                free(filewalk->file_dest);
                filewalk->file_dest = NULL;
            }
            free(filewalk);
            filewalk = filenext;
        }
    }

    if (info->pkg_installed) {
        struct PxlPackage_touched *filewalk, *filenext;
        
        filewalk = info->pkg_installed;
        info->pkg_installed = NULL;
        while (filewalk) {
            filenext = filewalk->next_file;
            if (filewalk->file_path) {
                free(filewalk->file_path);
                filewalk->file_path = NULL;
            }
            if (filewalk->file_hash) {
                free(filewalk->file_hash);
                filewalk->file_hash = NULL;
            }
            free(filewalk);
            filewalk = filenext;
        }
    }
    
    if (info->pkg_postflight) {
        struct PxlPackage_postflight *postwalk, *postnext;
        postwalk = info->pkg_postflight;
        info->pkg_postflight = NULL;
        while (postwalk) {
            postnext = postwalk->next_post;
            if (postwalk->post_command) {
                int loop;
                
                for (loop = 0; loop < postwalk->post_count; loop++) {
                    free(postwalk->post_command[loop]);
                }
                free(postwalk->post_command);
            }
            if (postwalk->post_createsFile) {
                free(postwalk->post_createsFile);
                postwalk->post_createsFile = NULL;
            }
            free(postwalk);
            postwalk = postnext;
        }
    }

    if (info->pkg_preremove) {
        struct PxlPackage_postflight *postwalk, *postnext;
        postwalk = info->pkg_preremove;
        info->pkg_preremove = NULL;
        while (postwalk) {
            postnext = postwalk->next_post;
            if (postwalk->post_command) {
                int loop;
                
                for (loop = 0; loop < postwalk->post_count; loop++) {
                    free(postwalk->post_command[loop]);
                }
                free(postwalk->post_command);
            }
            if (postwalk->post_createsFile) {
                free(postwalk->post_createsFile);
                postwalk->post_createsFile = NULL;
            }
            free(postwalk);
            postwalk = postnext;
        }
    }
    
    if (info->pkg_name) {
        free(info->pkg_name);
        info->pkg_name = NULL;
    }
    
    if (info->pkg_id) {
        free(info->pkg_id);
        info->pkg_id = NULL;
    }
    
    if (info->pkg_version) {
        free(info->pkg_version);
        info->pkg_version = NULL;
    }

    if (info->pkg_description) {
        free(info->pkg_description);
        info->pkg_description = NULL;
    }
    
    if (info->pkg_url) {
        free(info->pkg_url);
        info->pkg_url = NULL;
    }

    if (info->pkg_updateurl) {
        free(info->pkg_updateurl);
        info->pkg_updateurl = NULL;
    }
    
    if (info->pkg_icon) {
        free(info->pkg_icon);
        info->pkg_icon = NULL;
    }

    if (info->pkg_scratchdir) {
        free(info->pkg_scratchdir);
        info->pkg_scratchdir = NULL;
    }

    free(info);
}

struct PxlPackage_info * packageExtract(const char *package)
{
    unzFile uf;
    uLong i;
    unz_global_info gi;
    int err;
    char *packagename, *tptr;
    char *workdir;
    int length;
    struct PxlPackage_info *pkg;
    char *manifestFile;
    int manifestFileLen;
    
    packagename = NULL;
    
    pkg = NULL;
    
    uf = unzOpen(package);
    if (!uf) {
        log_printf("Error: Unable to open package %s", package);
        return NULL;
    }

    tptr = strrchr(package,'/');
    if (tptr) {
        packagename = strdup(tptr + 1);
    }
    else {
        packagename = strdup(package);
    }
    
    tptr = strrchr(packagename,'.');
    if (tptr) {
        *tptr = 0;
    }

    length = strlen(PXL_SCRATCH) + strlen(packagename) + 10;    
    workdir = (char *)malloc(length);
    snprintf(workdir,length - 1,"%s/%s.wrk", PXL_SCRATCH, packagename);
    
    mkdir(workdir,S_IRWXU | S_IRWXG | S_IRWXO);
    chdir(workdir);

    err = unzGetGlobalInfo (uf,&gi);
    if (err!=UNZ_OK) {
        log_printf("Error: Package %s is corrupt",packagename);
        unzClose(uf);
        free(packagename);
        free(workdir);
        return NULL;
    }

    for (i=0;i<gi.number_entry;i++)
    {
        if (uz_extract_currentfile(uf,NULL) != UNZ_OK)
            break;

        if ((i+1)<gi.number_entry)
        {
            err = unzGoToNextFile(uf);
            if (err!=UNZ_OK)
            {
                log_printf("Error: package %s is corrupted.",package);
                unzClose(uf);
                free(packagename);
                free(workdir);
                return NULL;
            }
        }
    }
    unzClose(uf);
    chdir(PXL_DROP_PATH);

    /* Okay, we are unzipped.  Now let us parse our manifest */    
    manifestFileLen = strlen(workdir) + strlen(PXL_MANIFEST) + 3;
    manifestFile = (char *)malloc(manifestFileLen);
    memset(manifestFile,0,manifestFileLen);
    snprintf(manifestFile,manifestFileLen - 1,"%s/%s", workdir, PXL_MANIFEST);

    if (!exists_file(manifestFile)) {
        DIR *checkdir;
        struct dirent *dp;

        free(manifestFile);
        manifestFile = NULL;

        /* Hrm.  Perhaps darkten made this zip!  Check if we have a subdirectory... */

        checkdir = opendir(workdir);
        if (checkdir) {
            while (!manifestFile && (dp = readdir(checkdir))) {
                if (dp->d_name && strcmp(dp->d_name,".") && strcmp(dp->d_name,"..")) {
                    int templen;
                    char *tempbuffer;
                    
                    templen = strlen(workdir) + strlen(dp->d_name) + strlen(PXL_MANIFEST) + 6;
                    tempbuffer = (char *)malloc(templen);
                    memset(tempbuffer,0,templen);
                    
                    snprintf(tempbuffer,templen - 1,"%s/%s/%s", workdir, dp->d_name, PXL_MANIFEST);
 
                    if (exists_file(tempbuffer)) {
                        char *newdir;
                        
                        /* Found the manifest.  Change our scratch directory and 
                         * our manifestfile variables */
                        manifestFile = strdup(tempbuffer);
                        newdir = (char *)malloc(templen);
                        memset(newdir,0,templen);
                        snprintf(newdir,templen - 1,"%s/%s", workdir, dp->d_name);
                        free(workdir);
                        workdir = newdir;
                    }
                    free(tempbuffer);
                }
            }
            closedir(checkdir);
        }
    }

    if (!manifestFile) {
        log_printf("Error: package %s does not seem to have a manifest file.",package);
        free(packagename);
        free(workdir);
        return NULL;    
    }
    
    pkg = parsePackageInfo(manifestFile);
    pkg->pkg_scratchdir = strdup(workdir);

    free(manifestFile);
    free(packagename);
    free(workdir);
        
    return pkg;
}

